#pragma once

#include <algorithm>
#include <jsoncpp/json/json.h>
#include <unordered_map>
#include "index.hpp"
#include "util.hpp"

namespace ns_searcher
{
    // 构建 doc_id 到 std::vector<std::string> 的映射
    struct InvertedElemPrint
    {
        uint64_t doc_id;
        std::vector<std::string> words;
        int weight;
        bool is_ad;
        double bid_price;
        double budget;

        InvertedElemPrint() : doc_id(0), weight(0), is_ad(false), bid_price(0.0), budget(0.0) {}
    };

    class Searcher
    {
    public:
        Searcher() {}
        ~Searcher() {}
        void InitSearcher(const std::string &input)
        {
            // 1. 获取或创建索引
            index = ns_index::Index::GetInstance();
            std::cout << "创建或获取单例成功..." << std::endl;
            // 2. 构建索引
            index->BuildIndex(input);
            std::cout << "构建正排和倒排索引成功..." << std::endl;
        }

        void Search(const std::string &query, std::string *json_string)
        {
            // 1. 对 query 进行分词
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query, &words);

            // 2. 拉取所有元素
            // ns_index::InvertedList inverted_lists;

            std::unordered_map<uint64_t, InvertedElemPrint> token_map;
            for (auto &word : words)
            {
                // 获取倒排拉链之前，将关键词转小写
                boost::to_lower(word);
                ns_index::InvertedList *inverted_list = index->GetInvertedIndex(word);
                if (inverted_list == nullptr)
                {
                    continue;
                }
                // inverted_lists.insert(inverted_lists.end(), inverted_list->begin(), inverted_list->end());

                for (const auto &item : *inverted_list)
                {
                    InvertedElemPrint &elem = token_map[item.doc_id];
                    elem.doc_id = item.doc_id;
                    elem.weight += item.weight;
                    elem.is_ad = item.is_ad;
                    elem.bid_price = item.bid_price;
                    elem.budget = item.budget;

                    elem.words.push_back(item.word);
                }
            }

            // 3. 对广告和非广告进行排序
            std::vector<InvertedElemPrint> ad_results;
            std::vector<InvertedElemPrint> organic_results;

            for (auto it = token_map.begin(); it != token_map.end(); it++)
            {
                if (it->second.is_ad == true && it->second.budget > 0)
                    ad_results.push_back(std::move(it->second));
                else    
                    organic_results.push_back(std::move(it->second));
            }
            
            // 3.1 对广告进行 竞价排名
            auto ad_sort = [](const InvertedElemPrint& e1, const InvertedElemPrint& e2) {
                return e1.bid_price > e2.bid_price;
            };
            sort(ad_results.begin(), ad_results.end(), ad_sort);

            // 确保广告不超过三条
            if (ad_results.size() > 3)
            {
                organic_results.insert(organic_results.end(), ad_results.begin()+3, ad_results.end());
            }
            // 3.2 对非广告排序
            auto organic_sort = [](const InvertedElemPrint& e1, const InvertedElemPrint& e2) {
                return e1.weight > e2.weight;
            };
            sort(organic_results.begin(), organic_results.end(), organic_sort);

            // 3.3 汇总
            std::vector<InvertedElemPrint> final_results;
            final_results.reserve(ad_results.size() + organic_results.size());

            final_results.insert(final_results.end(), ad_results.begin(), ad_results.end());
            final_results.insert(final_results.end(), organic_results.begin(), organic_results.end());

            // // 3. 根据权重排倒序 -- 需要将 inverted_lists 中的元素转到 vector 中再排序
            // std::vector<InvertedElemPrint> inverted_lists;
            // for (auto it = token_map.begin(); it != token_map.end(); it++)
            // {
            //     inverted_lists.push_back(std::move(it->second));
            // }

            // std::sort(inverted_lists.begin(), inverted_lists.end(),
            //           [](const InvertedElemPrint &e1, const InvertedElemPrint &e2)
            //           {
            //               return e1.weight > e2.weight;
            //           });

            // std::sort(inverted_lists.begin(), inverted_lists.end(),
            //           [](ns_index::InvertedElem &elem1, ns_index::InvertedElem &elem2)
            //           {
            //               return elem1.weight > elem2.weight;
            //           });

            // 4. 使用 Json
            Json::Value res;
            for (auto &elem : final_results)
            {
                // 4.1 找正排元素
                ns_index::Doc_t *doc = index->GetForwardIndex(elem.doc_id);
                if (doc == nullptr)
                {
                    continue;
                }

                // 4.2 序列化 doc
                Json::Value item;
                item["title"] = doc->title;
                item["desc"] = GetDesc(doc->content, elem.words[0]);
                item["url"] = doc->url;
                item["doc_id"] = (int)doc->doc_id; // 可能会出现数据截断
                item["weight"] = elem.weight;

                // 4.3 添加进 res
                res.append(item);
            }

            // 5. 序列化
            // Json::StyledWriter writer;
            Json::FastWriter writer;
            *json_string = writer.write(res);
        }

        void HandleAdClick(uint64_t doc_id)
        {
            std::unique_lock<std::mutex> lock(mtx);
            ns_index::Doc_t* doc = index->GetForwardIndex(doc_id);
            if (doc == nullptr || doc->is_ad == false || doc->budget <= 0)
                return;
            
            doc->budget -= doc->bid_price;
            if (doc->budget < 0) doc->budget = 0;
        }

    private:
        std::string GetDesc(const std::string &content, const std::string &word)
        {
            // 1. 定义固定步长 -- 使用 size_t 可能在计算中出现负数，导致异常情况
            const int PREV_STEP = 50;
            const int NEXT_STEP = 100;
            // 2. 查找 word 第一次出现位置
            auto it = std::search(content.begin(), content.end(), word.begin(), word.end(),
                                  [](int x, int y)
                                  {
                                      return std::tolower(x) == std::tolower(y);
                                  });

            if (it == content.end())
            {
                return "None-1";
            }
            int pos = std::distance(content.begin(), it);

            // 3. 计算 desc 的开始和结束位置
            int start = 0;
            int end = content.size() - 1;

            if (start + PREV_STEP < pos)
                start = pos - PREV_STEP;
            if (pos + NEXT_STEP < end)
                end = pos + NEXT_STEP;

            if (start >= end)
            {
                return "None-2";
            }

            // 4.
            std::string desc = content.substr(start, end - start);
            desc += "...";
            return desc;
        }

    private:
        std::mutex mtx;
        ns_index::Index *index;
    };
}

